#include <math.h>
#include <vector>
#include <sstream>
#include <algorithm>
#include <stdexcept>
#include "PizzaOrderParser.hpp"

namespace pizzeria {

PizzaOrderParser::PizzaOrderParser(const std::string &order)
    : mTokenizerPizza(order, ";")
{
  mOrder = order;
  mValidTokens[PIZZA_TYPE] = Pizza::GetListOfValidTypeString();
  mValidTokens[PIZZA_SIZE] = Pizza::GetListOfValidSizeString();
  Parse();
}

PizzaOrderParser::~PizzaOrderParser() {

}

std::list<Pizza> &PizzaOrderParser::GetPizzaList() {
  return mPizzaList;
}

std::list<Pizza> PizzaOrderParser::GetPizzaListFromOrder(
    const std::string &command) {
  PizzaOrderParser pizzaOrder(command);
  return pizzaOrder.GetPizzaList();
}

void PizzaOrderParser::Parse() {
  utils::Tokenizer::const_iterator it = mTokenizerPizza.begin();

  if (mTokenizerPizza.size() < 1)
    throw std::invalid_argument("Empty order");
  for (int i = 1; it != mTokenizerPizza.end(); ++it) {
    utils::Tokenizer tokenizerInfos(*it, " \t");
    if ((*it).empty() == false) {
      ParsePizza(tokenizerInfos, i);
      i++;
    }
  }
}

void PizzaOrderParser::ParsePizza(utils::Tokenizer &tokenizerInfos, int id) {
  utils::Tokenizer::const_iterator it = tokenizerInfos.begin();
  std::ostringstream oss;

  oss << "Pizza #" << id << ": ";
  if (tokenizerInfos.size() < 3)
    throw std::invalid_argument(oss.str() + "less than 3 arguments");
  if (tokenizerInfos.size() > 3)
    throw std::invalid_argument(oss.str() + "more than 3 arguments");
  Pizza::PizzaType pizzaType = ParsePizzaGetType(*it);
  it++;
  Pizza::PizzaSize pizzaSize = ParsePizzaGetSize(*it);
  it++;
  unsigned int pizzaAmout = ParsePizzaGetAmount(*it);
  for (; pizzaAmout > 0; pizzaAmout--) {
    mPizzaList.push_back(Pizza(pizzaType, pizzaSize));
  }
}

Pizza::PizzaType PizzaOrderParser::ParsePizzaGetType(
    std::string string) {
  std::list<std::string>::iterator it;

  std::transform(string.begin(), string.end(), string.begin(), tolower);
  it = std::find(mValidTokens[PIZZA_TYPE].begin(),
      mValidTokens[PIZZA_TYPE].end(), string);
  if (it == mValidTokens[PIZZA_TYPE].end()) {
    throw std::invalid_argument("`" + string + "' is not a valid pizza type");
  }
  else {
    std::list<std::string>::iterator itPos = mValidTokens[PIZZA_TYPE].begin();
    unsigned int i;
    for (i = 0; itPos != it; ++it, ++i)
      ;
    return static_cast<Pizza::PizzaType>(pow(2, i));
  }
}

Pizza::PizzaSize PizzaOrderParser::ParsePizzaGetSize(
    std::string string) {
  std::list<std::string>::iterator it;

  std::transform(string.begin(), string.end(), string.begin(), toupper);
  it = std::find(mValidTokens[PIZZA_SIZE].begin(),
      mValidTokens[PIZZA_SIZE].end(), string);
  if (it == mValidTokens[PIZZA_SIZE].end()) {
    throw std::invalid_argument("`" + string + "' is not a valid pizza size");
  }
  else {
    std::list<std::string>::iterator itPos = mValidTokens[PIZZA_SIZE].begin();
    unsigned int i;
    for (i = 0; itPos != it; ++it, ++i)
      ;
    return static_cast<Pizza::PizzaSize>(pow(2, i));
  }
}

unsigned int PizzaOrderParser::ParsePizzaGetAmount(
    std::string string) {
  std::transform(string.begin(), string.end(), string.begin(), tolower);
  if (string.length() > 0 && string[0] != 'x')
    throw std::invalid_argument(
	"`" + string
	    + "': bad format for pizza amount (should start with a 'x')");
  else if (string.length() < 2)
    throw std::invalid_argument(
	"`" + string
	    + "': bad format for pizza amount (expected a number after the 'x')");
  std::string s = string.substr(1);
  if (s[0] == '-')
    throw std::invalid_argument(
	"`" + string
	    + "': bad format for pizza amount (can not be negative)");
  if (s[0] == '+')
    s = s.substr(1);
  if (s.empty() || s.find_first_not_of("0123456789") != std::string::npos) {
    throw std::invalid_argument("`" + s + "' is not a valid integer");
  }
  std::istringstream iss(s);
  std::ostringstream oss;
  unsigned int amout;
  iss >> amout;
  oss << amout;
  if (amout > 100000)
    throw std::invalid_argument("`" + s + "' is too big (maximum: 100 000)");
  return amout;
}

} /* !namespace pizzeria */
